package db.domain;

import db.ObjectJsonConverter;
import db.UserResult;
import db.entitylistener.UserEntityListener;
import org.apache.ibatis.type.JdbcType;
import org.springframework.transaction.annotation.Propagation;
import sf.core.DBCascadeField;
import sf.core.DBField;
import sf.database.annotations.ColumnInfo;
import sf.database.annotations.Comment;
import sf.database.annotations.FetchDBField;
import sf.database.annotations.SavedDefaultValue;
import sf.database.annotations.Type;
import sf.database.jdbc.extension.ObjectJsonMapping;
import sf.mybatis.JacksonTypeHandler;
import sf.database.template.mybatis.MybatisResult;

import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EntityResult;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FieldResult;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
import javax.persistence.MapKeyClass;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import java.math.BigDecimal;
import java.sql.Types;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 用户类
 * @author SXF
 */
@EntityListeners({UserEntityListener.class})
@SqlResultSetMapping(name = "UserResult",
        entities = {
                @EntityResult(entityClass = UserResult.class, fields = {
                        @FieldResult(name = "metaResult.userId", column = "id"/*对应别名*/),
                        @FieldResult(name = "metaResult.icon", column = "icon"/*对应别名*/)
                }
                ),
                @EntityResult(entityClass = User.class, fields = {
                        @FieldResult(name = "id", column = "id")
                })
        },
        classes = {@ConstructorResult(targetClass = User.class, columns = {@ColumnResult(name = "id"), @ColumnResult(name = "login_name")})}
)
@Entity
@Table(name = "wp_users", indexes = {@Index(name = "user_idx", columnList = "loginName")},
        uniqueConstraints = {@UniqueConstraint(name = "user_uk", columnNames = {"login_name"})})
@Comment("用户表")
public class User extends PublicField {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @GeneratedValue
    @Column(name = "login_name", length = 60, nullable = false)
    private String loginName;// 登陆名

    @Column(length = 64)
    private String password;

    @Column(length = 50)
    private String nicename;

    @Comment("电子邮箱地址")
    @Column(length = 100)
    private String email;

    @Column(length = 100)
    private String url;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date registered;

    /**
     * 激活码
     */
    @Column(name = "activation_key", length = 60, nullable = false)
    private String activationKey;

    private int status;

    @Comment("性别")
    @Column
    @Enumerated(EnumType.STRING)
    private Sex sex;

    @Comment("年龄")
    @Column
    private Integer age;

    @Comment("事务级别")
    @Column
    @Enumerated(EnumType.STRING)
    private Propagation propagation;

    @Comment("显示名称")
    @Column(name = "display_name", length = 250)
    private String displayName;

    @Comment("头像")
    @Lob
    @Column
    private byte[] icon;

    @Comment("自我描述")
    @Lob
    @Column
    private String description;

    @Column
    private Boolean spam;

    @ColumnInfo(logicDelete = true, logicDeletedValue = "true")
    @Column
    private boolean deleted;

    @Column
    private int order;

    /**
     * 需要插入默认权重为0
     */
    @Column(precision = 10, scale = 5)
    @SavedDefaultValue
    private BigDecimal weight = new BigDecimal(0);

    @Transient
    private boolean lock;

    //Type和Convert可以同时写 也可以单独写
    @MybatisResult(jdbcType = JdbcType.CLOB, typeHandler = JacksonTypeHandler.class)
    @Column(name = "maps1", length = 1500)
    @Type(value = ObjectJsonMapping.class, mix = false)//有jdbc的自定义类型,可以通过设置mix解决和r2dbc共用的问题.
    @Convert(converter = ObjectJsonConverter.class)
    private Map<String, String> maps1;

    @Column(name = "maps2")
    @Type(javaType = String.class, sqlType = Types.CLOB)
    @Convert(converter = ObjectJsonConverter.class)//此写法兼容jdbc和r2dbc
    private Map<String, String> maps2;

    @ManyToMany
    @Transient
    @OrderBy("id asc,role desc")
    @JoinTable(name = "user_role", joinColumns = {
            @JoinColumn(name = "user_id", referencedColumnName = "id")}, inverseJoinColumns = {
            @JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<Role> roles;


    @OrderBy
    @Transient
    @FetchDBField({"id", "key"})
    @OneToMany(targetEntity = UserMeta.class)
    @JoinColumn(name = "id", referencedColumnName = "userId")
    private Set<UserMeta> userMetaSet = new LinkedHashSet<>();

    @OrderBy
    @OneToMany(targetEntity = UserMeta.class)
    @JoinColumn(name = "id", referencedColumnName = "userId")
    @MapKeyClass(Integer.class)
    @MapKey
    private Map<Integer, UserMeta> metaMap = new HashMap<>();

//    @OneToOne(targetEntity = UserMeta.class, mappedBy = "userId")
//    @JoinColumn(name = "id", referencedColumnName = "userId")
//    private UserMeta um;


    /**
     * 普通字段
     */
    public enum Field implements DBField {
        id, loginName, password, nicename, email, url, registered, activationKey, status, sex, age, propagation, displayName, icon, description,
        maps1, maps2,
        spam, deleted, order, weight, createBy, created, modifyBy, modified, ownerOrg, dataValid;
    }

    /**
     * 级联字段
     */
    public enum CascadeField implements DBCascadeField {
        roles, userMetaSet, metaMap
    }

    public User() {

    }

    public User(Long id, String loginName) {
        this.id = id;
        this.loginName = loginName;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        if (this._recordUpdate) {
            this.prepareUpdate(Field.id, id);
        }
        this.id = id;
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getNicename() {
        return nicename;
    }

    public void setNicename(String nicename) {
        this.nicename = nicename;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Date getRegistered() {
        return registered;
    }

    public void setRegistered(Date registered) {
        this.registered = registered;
    }

    public String getActivationKey() {
        return activationKey;
    }

    public void setActivationKey(String activationKey) {
        this.activationKey = activationKey;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Sex getSex() {
        return sex;
    }

    public void setSex(Sex sex) {
        this.sex = sex;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public byte[] getIcon() {
        return icon;
    }

    public void setIcon(byte[] icon) {
        this.icon = icon;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Boolean getSpam() {
        return spam;
    }

    public void setSpam(Boolean spam) {
        this.spam = spam;
    }

    public boolean isDeleted() {
        return deleted;
    }

    public void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }

    public BigDecimal getWeight() {
        return weight;
    }

    public void setWeight(BigDecimal weight) {
        this.weight = weight;
    }

    public boolean isLock() {
        return lock;
    }

    public void setLock(boolean lock) {
        this.lock = lock;
    }

    public Map<String, String> getMaps1() {
        return maps1;
    }

    public void setMaps1(Map<String, String> maps1) {
        this.maps1 = maps1;
    }

    public Map<String, String> getMaps2() {
        return maps2;
    }

    public void setMaps2(Map<String, String> maps2) {
        this.maps2 = maps2;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Set<UserMeta> getUserMetaSet() {
        return userMetaSet;
    }

    public void setUserMetaSet(Set<UserMeta> userMetaSet) {
        this.userMetaSet = userMetaSet;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public String getCreateBy() {
        return super.getCreateBy();
    }

    @Override
    public void setCreateBy(String createBy) {
        super.setCreateBy(createBy);
    }

    @Override
    public Date getCreated() {
        return super.getCreated();
    }

    @Override
    public void setCreated(Date created) {
        super.setCreated(created);
    }

    @Override
    public String getModifyBy() {
        return super.getModifyBy();
    }

    @Override
    public void setModifyBy(String modifyBy) {
        super.setModifyBy(modifyBy);
    }

    @Override
    public Date getModified() {
        return super.getModified();
    }

    @Override
    public void setModified(Date modified) {
        super.setModified(modified);
    }

    @Override
    public String getOwnerOrg() {
        return super.getOwnerOrg();
    }

    @Override
    public void setOwnerOrg(String ownerOrg) {
        super.setOwnerOrg(ownerOrg);
    }

    @Override
    public Boolean getDataValid() {
        return super.getDataValid();
    }

    @Override
    public void setDataValid(Boolean dataValid) {
        super.setDataValid(dataValid);
    }

    public Map<Integer, UserMeta> getMetaMap() {
        return metaMap;
    }

    public void setMetaMap(Map<Integer, UserMeta> metaMap) {
        this.metaMap = metaMap;
    }

    public Propagation getPropagation() {
        return propagation;
    }

    public void setPropagation(Propagation propagation) {
        this.propagation = propagation;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", loginName='" + loginName + '\'' +
                ", password='" + password + '\'' +
                ", nicename='" + nicename + '\'' +
                ", email='" + email + '\'' +
                ", url='" + url + '\'' +
                ", registered=" + registered +
                ", activationKey='" + activationKey + '\'' +
                ", status=" + status +
                ", sex=" + sex +
                ", propagation=" + propagation +
                ", displayName='" + displayName + '\'' +
                ", icon=" + Arrays.toString(icon) +
                ", description='" + description + '\'' +
                ", spam=" + spam +
                ", deleted=" + deleted +
                ", order=" + order +
                ", weight=" + weight +
                ", lock=" + lock +
                ", maps1=" + maps1 +
                ", maps2=" + maps2 +
                ", roles=" + roles +
                ", userMetaSet=" + userMetaSet +
                ", metaMap=" + metaMap +
                '}';
    }
}
